import { withDefaults, defineProps } from 'vue'

type NotUndefined<T> = T extends undefined ? never : T

type PropsWithDefaults<Base, Defaults> = Base &
  {
    [K in keyof Defaults]: K extends keyof Base ? NotUndefined<Base[K]> : never
  }

type Constructor =
  | MapConstructor
  | SetConstructor
  | DateConstructor
  | ArrayConstructor
  | ErrorConstructor
  | ProxyConstructor
  | BigIntConstructor
  | NumberConstructor
  | ObjectConstructor
  | RegExpConstructor
  | SymbolConstructor
  | StringConstructor
  | BooleanConstructor
  | PromiseConstructor
  | AsyncGeneratorFunctionConstructor
  | FinalizationRegistryConstructor
  | SharedArrayBufferConstructor
  | GeneratorFunctionConstructor
  | Uint8ArrayConstructor
  | Uint16ArrayConstructor
  | Uint32ArrayConstructor
  | Uint8ClampedArrayConstructor
  | ReferenceErrorConstructor
  | BigUint64ArrayConstructor
  | AggregateErrorConstructor
  | CustomElementConstructor
  | Float32ArrayConstructor
  | Float64ArrayConstructor
  | FinalizationRegistryConstructor
  | SyntaxErrorConstructor
  | WeakMapConstructor
  | WeakRefConstructor
  | WeakSetConstructor
  | DataViewConstructor
  | FunctionConstructor
  | URIErrorConstructor
  | EvalErrorConstructor
  | Int8ArrayConstructor
  | TypeErrorConstructor
  | Int16ArrayConstructor
  | Int32ArrayConstructor
  | RangeErrorConstructor
  | ArrayBufferConstructor

interface basePropsDefaultTypes<T> {
  type?: Constructor
  require?: boolean
  default?: (() => T extends Constructor ? Constructor : T) | T
  validator?: (val: T) => boolean
}
type property<T> = {
  [key in keyof T]: basePropsDefaultTypes<T>
}

export function useDefineProps<T extends object>(props: T | property<T>): PropsWithDefaults<Readonly<unknown>, T | property<T>> {
  return withDefaults(defineProps(), props)
}
